#include "com_sct_myopenglapp_PlayerJni.h"
#include <android/log.h>

#include <fstream>

#include "sct/core/common.h"
#include "sct/core/mutex.h"
#include "sct/rtspclient/rtspclientinterface.h"
#include "sct/decode/decodeinterface.h"

#include "screen.h"

#define SCT_TAG "sct"

using namespace sct;

class CPlayerInfo
{
public:
	sct::CMutex * _mutex;
	sct::CRTSPClientInterface * _rtspClient;
	int _state;
	pthread_t _thdRcv;
	pthread_t _thdDecode;
//	pthread_t _thdShow;
	CDecodeInterface * _decoder;
	std::ofstream * _fout;
//	uint8_t * _data4show;
};

enum PlayerState
{
	PS_Terminated = SCT_ENUM_OFFSET,
	PS_Inited,
	PS_Decoding,
	PS_Stopped,
};

static CPlayerInfo * gPlayerInfo = NULL;

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
	JNIEnv* env = NULL;
	jint ret = JNI_ERR;

	if (vm->GetEnv((void**) &env, JNI_VERSION_1_2) != JNI_OK)
	{
		__android_log_print(ANDROID_LOG_ERROR, SCT_TAG, "GetEnv failed!");
		return ret;
	}

	gPlayerInfo = new CPlayerInfo;
	gPlayerInfo->_mutex = new CMutex;
	gPlayerInfo->_state = PS_Terminated;
	gPlayerInfo->_rtspClient = NULL;
	gPlayerInfo->_decoder = NULL;
	gPlayerInfo->_fout = NULL;
//	gPlayerInfo->_data4show = NULL;

	ret = JNI_VERSION_1_2;

	__android_log_print(ANDROID_LOG_VERBOSE, SCT_TAG, "Init base env is ok");

	return ret;
}

static void* recv_thread(void* arg)
{
	while(PS_Decoding == gPlayerInfo->_state)
	{
		usleep(1000);

		static double tPre;
		double t = currentTime();
		t -= tPre;
		if(t > 0.08)
		{
			SCT_LOG("recv too slow accurate!!!!:%f", t);
		}
		tPre = sct::currentTime();


		gPlayerInfo->_rtspClient->demux();
	}

	return NULL;
}

//static void* show_thread(void* arg)
//{
//	while(PS_Decoding == gPlayerInfo->_state)
//	{
//		usleep(1000);
//
////		int w, h;
////		int ret;
////		ret = gPlayerInfo->_decoder->getData(gPlayerInfo->_data4show, 1920*1920*4, &w, &h);
////
////		if(ret > 0)
////		{
////			sct_screen_putdata(gPlayerInfo->_data4show, ret, w, h);
////		}
//	}
//
//	return NULL;
//}

enum PlayBufferState
{
	PBS_DropAndLookForI = SCT_ENUM_OFFSET,
	PBS_Buffering,
	PBS_Playing,
};

static void* decode_thread(void* arg)
{
	uint8_t * buf = new uint8_t[2000000];
	uint8_t * bufAdj = new uint8_t[2000000];
	int len;
	int64_t pts;
	uint32_t dropFramePre;

//	int bBuffering = 1;
//	int bDropFrame = 0;
	int state = PBS_DropAndLookForI;

	double nxtPlayTime = 0;
	int64_t streamTime = 0;

	int tBaseBufferTime =   90000;
	int tMaxBufferTime =  1000000;

	SCT_LOG("base time:%d    max time:%d", tBaseBufferTime, tMaxBufferTime);

	while(PS_Decoding == gPlayerInfo->_state)
	{
//		double ty1 = currentTime();
//
//		//usleep(1000);
//
//		double ty2 = currentTime();
//		ty2 -= ty1;
//		if(ty2 > 0.01)
//		{
//			SCT_LOG("PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP:%f", ty2);
//		}
//
//
//		double ty3 = currentTime();
//		///<------------------
//		int w, h;
//		int ret;
////		ret = gPlayerInfo->_decoder->getData(gPlayerInfo->_data4show, 1920*1920*4, &w, &h);
//
//		if(ret > 0)
//		{
//			//sct_screen_putdata(gPlayerInfo->_data4show, ret, w, h);
//		}
		///end

//		len = gPlayerInfo->_rtspClient->getVideoData(&pts, buf, 2000000, NULL);
//		if(len > 0)
//		{
//		   uint32_t head;
//		   memcpy(&head, buf, 4);
//		   if(head != 0x1000000 )
//		   {
//			   head = 0x1000000;
//			   memcpy(bufAdj, &head, 4);
//			   memcpy(bufAdj+4, buf, len);
//			   len += 4;
//		   }
//		   else
//		   {
//			   memcpy(bufAdj, buf, len);
//		   }
//
//
//			if(gPlayerInfo->_fout)
//			{
//				gPlayerInfo->_fout->write((char*)bufAdj, len);
//			}
//		}
//		continue;

//		static double tPre;
//		double tt = sct::currentTime() - tPre;
//		if(tt > 0.08)
//		{
//			SCT_LOG("time un accurate!!!!%f", tt);
//		}
//		tPre = sct::currentTime();


        int64_t rtDelay = gPlayerInfo->_rtspClient->realTimeDelay();

        if(PBS_DropAndLookForI == state && rtDelay > tBaseBufferTime)
        {
		   len = gPlayerInfo->_rtspClient->getVideoData(&pts, buf, 2000000, NULL);
		   if(len > 0)
		   {
			   uint32_t head;
			   memcpy(&head, buf, 4);
			   if(head != 0x1000000 )
			   {
				   head = 0x1000000;
				   memcpy(bufAdj, &head, 4);
				   memcpy(bufAdj+4, buf, len);
				   len += 4;
			   }
			   else
			   {
				   memcpy(bufAdj, buf, len);
			   }


			   int nalu_type = (* (bufAdj+4) )&0x1f;
			   if( 7 == nalu_type || 8 == nalu_type || 5 == nalu_type )
			   {
				   state = PBS_Buffering;

				   SCT_LOG(".............stop............dropframe......stage1@@....type:%d, delay:%lld", nalu_type, rtDelay);

				   ///decode one frame
				   //SCT_LOG("decode one frame");
				   gPlayerInfo->_decoder->decode(bufAdj, len);
				   if(gPlayerInfo->_fout)
				   {
					   gPlayerInfo->_fout->write((char*)bufAdj, len);
					   SCT_LOG("start write file...");
				   }

				   ///sync
				   nxtPlayTime = sct::currentTime() + 0.02;
				   gPlayerInfo->_rtspClient->peekPTS(&streamTime);


//				   double ty4 = currentTime();
//				           ty4 -= ty3;
//				           if(ty4 > 0.08)
//				           {
//				           	SCT_LOG("PPPPPPPPPPPPPPPPPPPPPPYYYYYYYYYYY32323YY222YYYYYYYYYYYYY:%f", ty4);
//				           }

				   continue;///if you not, will cause frame lost.
			   }
		   }

		   //SCT_LOG("PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP one");
		}




        if(PBS_Buffering == state)
        {
            if(rtDelay > tBaseBufferTime)
            {
                SCT_LOG(".......cc2.Stop....Buffering...............");

                state = PBS_Playing;
            }
        }

        if(PBS_Playing == state && rtDelay < 10000)
        {
            SCT_LOG(".........start.......Buffering.......................:%lld", rtDelay);
            state = PBS_Buffering;
        }

        if(PBS_Playing == state && rtDelay > tMaxBufferTime) {
            //drop some frame. cause the decode is so slow
            SCT_LOG("XXXX..........start.......  .dropframe...............:%lld", rtDelay);
            state = PBS_DropAndLookForI;
        }


        if(PBS_Playing == state)
        {
            double cur = sct::currentTime();
            if (cur > nxtPlayTime ) {
                len = gPlayerInfo->_rtspClient->getVideoData(&pts, buf, 2000000, &dropFramePre);
                if(len > 0)
                {
                    if(dropFramePre)
                    {
                        state = PBS_DropAndLookForI;
                        SCT_LOG("XXXX......................lost frame detected!");
                        continue;
                    }

                    ///to decode one frame
                    uint32_t head;
                    memcpy(&head, buf, 4);
                    if(head != 0x1000000 )
                    {
                        head = 0x1000000;
                        memcpy(bufAdj, &head, 4);
                        memcpy(bufAdj+4, buf, len);
                        len += 4;
                    }
                    else
                    {
                        memcpy(bufAdj, buf, len);
                    }

                    ///decode one frame
                    //SCT_LOG("decode one frame");
                    gPlayerInfo->_decoder->decode(bufAdj, len);
                    if(gPlayerInfo->_fout)
				   {
					   gPlayerInfo->_fout->write((char*)bufAdj, len);
				   }


                    //calculate next systime
                    int64_t nxtPTS;
                    int ret;
                    ret = gPlayerInfo->_rtspClient->peekPTS(&nxtPTS);
                    if (ret < 0) {
                        //force play out
                        SCT_LOG("no next data, try to buffering...");
                        state = PBS_Buffering;
                        //no need to update sysTime and stream Time.
                    }
                    else {
                        nxtPTS -= streamTime;
                        double d = nxtPTS / 1000000.0;

                        if (d < 0) {
                            //fixme: should has better way
                            SCT_LOG("XXX...................error, may cause by pts wrap..., try to drop packet");
                            state = PBS_DropAndLookForI;
                        }

                        if( d > 0.5)
                        {
                            SCT_LOG("D is too big:%d, some thing wrong?", d);
                            d = 0.04;
                        }

                        if(d > 0.02 && rtDelay > tBaseBufferTime + 200000)
                        {
                        	//SCT_LOG("---------->%f", d);
                        	double d1 = d;
                        	d -= 0.005;
                        	SCT_LOG("---------->%f %f", d1, d);

                        }


                        nxtPlayTime = nxtPlayTime + d;
                        gPlayerInfo->_rtspClient->peekPTS(&streamTime);
                    }
                }
            }
        }
//        double ty4 = currentTime();
//        ty4 -= ty3;
//        if(ty4 > 0.08)
//        {
//        	SCT_LOG("PPPPPPPPPPPPPPPPPPPPPPYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY:%f", ty4);
//        }


	}


    SCT_DELETE_ARRAY(buf);
    SCT_DELETE_ARRAY(bufAdj);

	return NULL;
}

JNIEXPORT void JNICALL Java_com_sct_myopenglapp_PlayerJni_show
  (JNIEnv *, jclass)
{
	__android_log_print(ANDROID_LOG_VERBOSE, SCT_TAG, "%s", "jni show hh");

	CAutoLock locker(gPlayerInfo->_mutex);
	if(PS_Terminated != gPlayerInfo->_state) return;

	gPlayerInfo->_rtspClient = sct::CRTSPClientInterface::create("rtspclient");
	gPlayerInfo->_rtspClient->setMaxDelay(5000000);

	gPlayerInfo->_decoder = CDecodeInterface::create("decode");

	//gPlayerInfo->_fout = new std::ofstream("/storage/sdcard0/h264stream.h264");

//	gPlayerInfo->_data4show = new uint8_t[1920 * 1920 * 4];

	gPlayerInfo->_decoder->setPin(sct_screen_getPin());

	gPlayerInfo->_state = PS_Inited;

	gPlayerInfo->_state = PS_Decoding;

	pthread_attr_t attr;
	(pthread_attr_init(&attr));
	struct sched_param param;
	param.sched_priority = 60;
//	pthread_attr_set
	//(pthread_attr_setinheritsched(&attr,PTHREAD_EXPLICIT_SCHED));
	(pthread_attr_setschedpolicy(&attr,SCHED_RR));
	(pthread_attr_setschedparam(&attr,&param));

	pthread_create(&gPlayerInfo->_thdRcv, NULL, recv_thread, NULL);
	pthread_create(&gPlayerInfo->_thdDecode, &attr, decode_thread, NULL);
//	pthread_create(&gPlayerInfo->_thdShow, NULL, show_thread, NULL);
}

JNIEXPORT void JNICALL Java_com_sct_myopenglapp_PlayerJni_close
  (JNIEnv *, jclass)
{
	__android_log_print(ANDROID_LOG_VERBOSE, SCT_TAG, "%s", "jni close");
	CAutoLock locker(gPlayerInfo->_mutex);
	if(PS_Terminated == gPlayerInfo->_state) return;

	gPlayerInfo->_state = PS_Stopped;
//	pthread_join(gPlayerInfo->_thdShow, NULL);
	pthread_join(gPlayerInfo->_thdRcv, NULL);
	pthread_join(gPlayerInfo->_thdDecode, NULL);

	SCT_DELETE(gPlayerInfo->_rtspClient);
	SCT_DELETE(gPlayerInfo->_decoder);
	SCT_DELETE(gPlayerInfo->_fout);

//	SCT_DELETE_ARRAY(gPlayerInfo->_data4show);

	gPlayerInfo->_state = PS_Terminated;
}



JNIEXPORT void JNICALL Java_com_sct_myopenglapp_PlayerJni_testFunc
(JNIEnv *, jclass)
{
	__android_log_print(ANDROID_LOG_VERBOSE, SCT_TAG, "%s", "playerjni testfun");
}

